
#include <time.h>
#include <stdio.h>
#include <BPainter>
#include <BKeyEvent>
#include <BCharEvent>
#include <BEvent>
#include <BMouseEvent>
#include <BFocusEvent>
#include <BChangeEvent>
#include <BFont>
#include <BRange>
#include <BStyle>

#include <BLineEdit>
#include "member_BLineEdit.h"

using namespace BWE;

#define member					(*(member_BLineEdit*)_ptr)
#define member_allocate()		_ptr = new member_BLineEdit(this)
#define member_release()		delete (member_BLineEdit*)_ptr

BLineEdit::BLineEdit(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BLineEdit::BLineEdit(const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setText(text);
}
BLineEdit::~BLineEdit()
{
	member_release();
}

void BLineEdit::setText(const BString& text)
{
	const BString& current = this->text();
	if (current != text)
	{
		member.reset(text);
		member.changed = false;
		emit(Signal_TextChanged, member.text);
		this->fresh();
	}
}
const BString& BLineEdit::text() const
{
	return member.text;
}

void BLineEdit::setAlign(Align align)
{
	if(member.align != align)
	{
		member.align = align;
		member.dirty = true;
		this->fresh();
	}
}
Align BLineEdit::align() const
{
	return member.align;
}

BSize BLineEdit::sizeHint() const
{
	BSize hintSize;
	const BFont* font = this->font();
	if (font)
	{
		hintSize.setHeight(bMax(hintSize.height(), font->height()));
		hintSize.setWidth(font->height() * 8);
	}
	return this->shellSize() + hintSize;
}

void BLineEdit::updateEvent(const BEvent& event)
{
	if (this->focus() && !member.readOnly)
	{
		bool inputShown = member.inputShown;
		if (member.blinkTime > 0)
		{
			clock_t now = clock();
			if (now - member.blinkTick > member.blinkTime)
			{
				member.blinkTick = now;
				inputShown = !member.inputShown;
			}
		}
		else
		{
			inputShown = true;
		}
		if (member.inputShown != inputShown)
		{
			member.inputShown = inputShown;
			this->fresh();
		}
	}
}
void BLineEdit::resizeEvent(const BEvent& event)
{
	const BRect& crect = this->clientRect();
	member.width = crect.width();
	member.dirty = true;
}

void BLineEdit::styleEvent(const BEvent& changeEvent)
{
	member.refresh((BFont*)this->font());
	member.dirty = true;
}
void BLineEdit::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
}
void BLineEdit::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawContent();

	if (this->focus() && member.inputCount > 0)
	{
		int left = member.cells[member.inputIndex].rect.x();
		int right = member.cells[member.inputIndex + member.inputCount].rect.x();
		painter.setColor(Color_Background_Checked);
		BPoint offset = member.rect.pos();
		for (int i = 0; i < member.cells.size(); i++)
		{
			const CodeCell& cell = member.cells[i];
			painter.fillRect(cell.rect + offset);
		}
	}

	if (painter.setColor(Color_Text))
	{
		BPoint offset = member.rect.pos();
		for (int i = 0; i < member.cells.size(); i++)
		{
			const CodeCell& cell = member.cells[i];
			if (cell.image)
			{
				painter.drawImage(cell.image_rect + offset, cell.image);
			}
		}
	}
	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	if (member.inputShown && member.inputCount == 0)
	{
		int x = member.rect.x();
		int y = member.rect.center().y();
		int hh = member.rect.height();
		if (member.cells.size())
		{
			if (member.inputIndex < member.cells.size())
			{
				BRect cellRect = member.cells[member.inputIndex].rect + member.rect.pos();
				x = cellRect.left();
			}
			else
			{
				BRect cellRect = member.cells.last().rect + member.rect.pos();
				x = cellRect.right();
			}
		}
		if (painter.setColor(Color_Indicator))
		{
			painter.drawLine(x, y - hh, x, y + hh);
			painter.drawLine(x - 2, y - hh, x + 2, y - hh);
			painter.drawLine(x - 2, y + hh, x + 2, y + hh);
		}
	}

}

void BLineEdit::focusEvent(const BFocusEvent& focusEvent)
{
	if (focusEvent.focus() == Focus_In)
	{
		member.inputShown = true;
		member.justFocused = true;
		member.inputIndex = 0;
		member.inputCount = member.cells.size();
		this->fresh();
	}
	if (focusEvent.focus() == Focus_Out)
	{
		member.inputShown = false;
		if (member.changed)
		{
			member.freahText();
			emit(Signal_EditFinished, member.text);
		}
		this->fresh();
	}
}
void BLineEdit::keyDownEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	switch (key)
	{
	case Key_Left:
	{
		if (member.inputCount > 0)
		{
			member.inputCount = 0;
			this->fresh();
		}
		else if (member.inputIndex > 0)
		{
			member.inputIndex--;
			this->fresh();
		}
		member.blinkTick = clock() + 500;
		break;
	}
	case Key_Right:
	{
		if (member.inputCount > 0)
		{
			member.inputIndex += member.inputCount;
			member.inputCount = 0;
			this->fresh();
		}
		else if (member.inputIndex < member.cells.size())
		{
			member.inputIndex++;
			this->fresh();
		}
		member.blinkTick = clock() + 500;
		break;
	}
	case Key_Back:
	{
		if (member.inputCount > 0)
		{
			if (member.cells.remove(member.inputIndex, member.inputCount))
			{
				member.inputCount = 0;
				member.changed = true;
				member.dirty = true;
				member.freahText();
				emit(Signal_Edited, member.text);
				this->fresh();
			}
		}
		else
		{
			member.inputIndex--;
			if (member.cells.remove(member.inputIndex))
			{
				member.changed = true;
				member.dirty = true;
				member.freahText();
				emit(Signal_Edited, member.text);
				this->fresh();
			}
		}
		member.blinkTick = clock() + 500;
		break;
	}
	case Key_Delete:
	{
		if (member.inputCount > 0)
		{
			if (member.cells.remove(member.inputIndex, member.inputCount))
			{
				member.inputCount = 0;
				member.changed = true;
				member.dirty = true;
				member.freahText();
				emit(Signal_Edited, member.text);
				this->fresh();
			}
		}
		else
		{
			if (member.cells.remove(member.inputIndex))
			{
				member.changed = true;
				member.dirty = true;
				member.freahText();
				emit(Signal_Edited, member.text);
				this->fresh();
			}
		}
		member.blinkTick = clock() + 500;
		break;
	}
	case Key_Enter:
	{
		this->setFocus(false);
		break;
	}
	}
}
void BLineEdit::keyUpEvent(const BKeyEvent& keyEvent)
{
	this->fresh();
}
void BLineEdit::charEvent(const BCharEvent& charEvent)
{
	BCode code = (short)charEvent.code();
	char* ch = (char*)&code;
	if (ch[1] < 0 || (31 < code && code < 127))
	{
		if (member.inputCount > 0)
		{
			member.cells.remove(member.inputIndex, member.inputCount);
			member.inputCount = 0;
		}
		if (member.insert(member.inputIndex, code))
		{
			member.inputIndex++;
			member.changed = true;
			member.dirty = true;
			member.freahText();
			emit(Signal_Edited, member.text);
			this->fresh();
		}
	}
}

void BLineEdit::mouseEnterEvent(const BMouseEvent& event)
{
	member.hoverIndex = 0;
}
void BLineEdit::mouseLeaveEvent(const BMouseEvent& event)
{
	member.hoverIndex = 0;
}

void BLineEdit::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (member.justFocused)
	{
		member.justFocused = false;
		this->fresh();
		return;
	}
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		int hoverIndex = member.hoverIndex;
		if (member.inputCount > 0 || member.inputIndex != member.hoverIndex)
		{
			member.inputIndex = member.hoverIndex;
			member.inputCount = 0;
			this->fresh();
		}
		member.pressed = true;
		member.pressedIndex = member.hoverIndex;
	}
}
void BLineEdit::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	member.pressed = false;
	this->fresh();
}
void BLineEdit::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	int index = 0;
	for (int i = 0; i < member.cells.size(); i++)
	{
		const BRect& rect = member.cells[i].rect;
		if (rect.center().x() > pos.x())
		{
			index = i;
			break;
		}
		index++;
	}
	if (member.hoverIndex != index)
	{
		member.hoverIndex = index;
	}
	if (member.pressed)
	{
		member.inputIndex = bMin(member.pressedIndex, member.hoverIndex);
		member.inputCount = abs(member.pressedIndex - member.hoverIndex);
		this->fresh();
	}
}
void BLineEdit::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	BWidget::mouseDoubleClickEvent(mouseEvent);
	if (this->focus())
	{
		member.inputIndex = 0;
		member.inputCount = member.cells.size();
		this->fresh();
	}
}

